package org.zerta.troops.network;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;

import org.zerta.troops.common.Area;
import org.zerta.troops.common.Game;
import org.zerta.troops.common.NetworkMessage;
import org.zerta.troops.common.Player;
import org.zerta.troops.common.Strings;
import org.zerta.troops.common.TurnData;
import org.zerta.troops.server.ServerConsole;


public class TroopsClientConnection implements Runnable {

	TroopsNetworkServer troopsNetworkServer;
	Game game;
	Socket socket;
	ServerConsole console;
	
	
	public TroopsClientConnection ( TroopsNetworkServer troopsNetworkServer, Game game, Socket socket, ServerConsole console ) {
		this.troopsNetworkServer = troopsNetworkServer;
		this.game = game;
		this.socket = socket;
		this.console = console;
	}

	@Override
	public void run() {
		console.log("Client connected");
		try {
			Object inObject;
			ObjectOutputStream outStream;
			ObjectInputStream inStream;
			NetworkMessage request = null;
			NetworkMessage response;
			
			try {
				outStream = new ObjectOutputStream(socket.getOutputStream());
				outStream.flush();
			} catch (IOException e) {
				System.err.println("Failed to create outStream");
				e.printStackTrace(System.err);
				return;
			}
			
			try {
				inStream = new ObjectInputStream(socket.getInputStream());
			} catch (IOException e) {
				System.err.println("Failed to create inStream");
				e.printStackTrace(System.err);
				return;
			}

			try {
				inObject = inStream.readObject();
			} catch (ClassNotFoundException e) {
				System.err.println("Failed to readObject because of ClassNotFoundException");
				e.printStackTrace(System.err);
				return;
			} catch (IOException e) {
				System.err.println("Failed to readObject because of IOException");
				e.printStackTrace(System.err);
				return;
			}
			
			if ( inObject instanceof NetworkMessage ) {
				request = (NetworkMessage) inObject;
			}
			
			response = handleRequest(request);
			
			if (request.isSynchronized()) {
				try {
					outStream.writeObject(response);			
					outStream.flush();
				} catch (IOException e) {
					System.err.println("Failed to writeObject because of IOException");
					e.printStackTrace(System.err);
					return;
				}
			}

		    console.log("Closing all sockets");
		    socket.close();
		    
		} catch (IOException e) {
			System.err.println("Failed during main run loop");
			e.printStackTrace(System.err);
		}
	}
	
	public NetworkMessage handleRequest( NetworkMessage message ) {
		if (message.getId().equals(Strings.Network.JOIN_GAME_AS_PLAYER)) {
			Player player = new Player();
			player.setName((String) message.getObject());
			boolean playerAdded = game.addPlayer(player);
			if (playerAdded) {
				// Find first area
				List<Area> areaList = player.getAreasWithPresence();
				Area givenArea = null;
				for (Area area: areaList) {
					givenArea = area;
					break;
				}
				return new NetworkMessage(Strings.Network.JOIN_GAME_AS_PLAYER_ACK, givenArea, false);
			} else {
				return new NetworkMessage(Strings.Network.JOIN_GAME_AS_PLAYER_NACK, null, false);
			}
		} else if (message.getId().equals(Strings.Network.SEND_TURN_DATA)) {
			TurnData turnData = (TurnData) message.getObject();
			game.addTurnData(turnData);
			Object lock = troopsNetworkServer.getLock();
			synchronized(lock) {
				lock.notifyAll();
			}
			return new NetworkMessage(Strings.Network.SEND_TURN_DATA_OK, Strings.Network.SEND_TURN_DATA_OK, false);
		}
		return new NetworkMessage("DEFAULT", "DEFAULT", false);
	}
	
	public Game getGame() {
		return game;
	}

}
